Regex-based text alignment
Some people like to align program statements, for example, so that the "=" in assignments are lined up. This tip provides a script that defaults to aligning "=", but which can align using a search pattern. The script may not work correctly when tab characters are present. Examples For example, consider the lines: let range = '%' let foo = 'Vim' let something = 42 With this tip, if you visually select the lines then press \a (assuming the default backslash leader key), the lines would be changed to: let range = '%' let foo = 'Vim' let something = 42 Now consider: int Add(int a, int b); void Point(doublePt dp); const *char GetString(unsigned int index); Selecting these lines, then entering the command :Align \S\+( would align the function names. Repeating, with command :Align ( would then align the (. Doing both results in: int Add (int a, int b); void Point (doublePt dp); const *char GetString (unsigned int index); Script Save the following in a file, say, align.vim. Enter the command :so align.vim to source the script. command! -nargs=? -range Align ,call AlignSection() vnoremap a :Align function! AlignSection(regex) range let extra = 1 let sep = empty(a:regex) ? '=' : a:regex let maxpos = 0 let section = getline(a:firstline, a:lastline) for line in section let pos = match(line, ' *'.sep) if maxpos < pos let maxpos = pos endif endfor call map(section, 'AlignLine(v:val, sep, maxpos, extra)') call setline(a:firstline, section) endfunction function! AlignLine(line, sep, maxpos, extra) let m = matchlist(a:line, '\(.\{-}\) \{-}\('.a:sep.'.*\)') if empty(m) return a:line endif let spaces = repeat(' ', a:maxpos - strlen(m1) + a:extra) return m1 . spaces . m2 endfunction Related tips *We have a number of tips which use the terms "align", "justify", "format" in various, often inconsistent, ways. Need to check each tip and see they use the words correctly. *Need to clean up following tips. Some might be merged, but want to avoid undue complexity. '''Align text using Vim' *139 Align text plugin describes the plugin (script 294) *253 Specify a column with bar *319 Simple text alignment *893 Align numbers at decimal point *894 Regex-based text alignment this tip Align text using an external script *570 Align text into a table Perl script intend merging this to 139 Align tables using Vim *547 Smarter Table Editing *554 Smarter Table Editing II Invoke an external text formatting tool *584 Par text reformatter Comments See Align text plugin which describes the plugin. Separators are regular expression patterns, and one may restrict Align to operate only on lines which satisfy (or don't satisfy) a regular expression pattern (:AlignCtrl g pattern or :AlignCtrl v pattern). Alternative script *Examine following. Is it worth keeping? I decided to extend the tip's functionality to be able to do left, right and shift alignments. Shift alignments are basically indentation alignments that are useful for aligning decimal points on numbers. Align() also allows alignment of words before and after the regex pattern. For words before the pattern, enter a positive number when prompted. For words after the pattern, enter a negative number instead. For example, to choose the second word after the regex pattern, enter -2. After that, select the type of alignment to be done. "place in vimrc - tested on gvim 6.3 set magic "align code - select lines with Visual Block using vmap :call Align(Prompt("0"),Prompt("1","0"),Prompt("2","l")) "align code function function Align(regex, wnum, align) range let range = a:firstline.",".a:lastline let curcol = 0 let maxcol = 0 "find maximum column let words = Words(a:wnum, a:align, "find") let i = a:firstline while i <= a:lastline let line = getline(i) if line =~ a:regex if a:wnum < 0 let curcol = matchend(line, a:regex.words) else let curcol = match(line, words.a:regex) endif let maxcol = curcol > maxcol ? curcol : maxcol endif let i = i + 1 endwhile "perform alignment let i = a:firstline while i <= a:lastline let line = getline(i) if line =~ a:regex if a:wnum < 0 let curcol = matchend(line, a:regex.words) else let curcol = match(line, words.a:regex) endif let pad = "" while strlen(pad) < (maxcol - curcol) let pad = pad." " endwhile "determine padding location let words2 = Words(a:wnum, a:align, "pad") if a:wnum < 0 let curcol = matchend(line, a:regex.words2) else let curcol = match(line, words2.a:regex) endif "left-word or shift aligned if a:align "s" call setline(i, pad.strpart(line, 0, curcol).strpart(line, curcol)) else call setline(i, strpart(line, 0, curcol).pad.strpart(line, curcol)) endif endif let i = i + 1 endwhile execute "normal gv" endfunction "set up words regular expression function Words(wnum, align, action) if a:align "r" if a:action "find" if a:wnum > 0 let words = "\\(\\S*\\s*\\)\\{".a:wnum."}" elseif a:wnum < -1 let words = "\\(\\s*\\S*\\)\\{".(-a:wnum)."}" elseif a:wnum -1 let words = "\\s*\\S*" else let words = "" endif elseif a:action "pad" if a:wnum >= 0 let words = "\\(\\S*\\s*\\)\\{".(a:wnum + 1)."}" elseif a:wnum < -1 let words = "\\s*\\(\\S*\\s*\\)\\{".(-a:wnum - 1)."}" elseif a:wnum -1 let words = "\\s*" else let words = "" endif endif else if a:wnum > 0 let words = "\\(\\S*\\s*\\)\\{".a:wnum."}" elseif a:wnum < -1 let words = "\\s*\\(\\S*\\s*\\)\\{".(-a:wnum - 1)."}" elseif a:wnum -1 let words = "\\s*" else let words = "" endif endif return words endfunction "prompt user for alignment settings function Prompt(str, ...) if a:str if a:str "1" let str = "How many words before pattern? " elseif a:str "2" let str = "Alignment (r)ight (s)hift? " endif else let str = "Pattern? " endif let default = a:0 ? a:1 : "" execute "let ret = input(\"".str."\", \"".default."\")" return ret endfunction ----